package fdts.data;

/********************************************************
 * FDTS SS 2012. Calendar Layouter and iCalendar Imorter*
 * ******************************************************/

import static timeBench.data.TemporalDataset.PRIMITIVE_INTERVAL;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;

import prefuse.data.io.DataIOException;

import fdts.calendar.layout.CalendarAxis;
import fdts.data.io.iCalendarImporter;

import timeBench.calendar.Granularity;
import timeBench.calendar.Granule;
import timeBench.calendar.JavaDateCalendarManager;
import timeBench.calendar.JavaDateCalendarManager.Granularities;
import timeBench.data.TemporalDataException;
import timeBench.data.TemporalDataset;
import timeBench.data.TemporalElement;
import timeBench.data.TemporalObject;

/**
 * this class generates the data and date for the CalendarVisualization application
 * 
 * @author agnieszka wojdecka and andreas hoermann
 *
 */
public class DataReader{
	/**
	 * this method generates the timebench.data.TemporalDataset object.
	 * it stores all data of the calendar for the chosen granularity with respect 
	 * to the axes-specification of the granularity. 
	 * @param path path to ics file
	 * @param grName the name of the granularity
	 * @param axes the axes definitions
	 * @param today milliseconds of the current time in the calendar
	 * @return timebench.data.TemporalDataset object with the data for CalendarVisualization application
	 * @throws ParseException
	 * @throws TemporalDataException
	 * @throws DataIOException
	 */
	public TemporalDataset generateTable(String path, String grName, HashMap<String,ArrayList<CalendarAxis>> axes, long today) throws ParseException, TemporalDataException, DataIOException{
		String from = ""; // the starting point to import data, in local time format
		String to = ""; // the endpoint to import data, in local time format
		
		GregorianCalendar cal = new GregorianCalendar();
		cal.setTimeInMillis(today);
		
		// generate from and to depending on the granualrity
		if(grName.equals("year")){
			from += (cal.get(GregorianCalendar.YEAR) + "0101T000000" );
			to += (cal.get(GregorianCalendar.YEAR) + "1231T235959" );
		} else if(grName.equals("month")){
			int month = cal.get(GregorianCalendar.MONTH);
			if((month+1) < 10){
				from += cal.get(GregorianCalendar.YEAR) + "0" + (month+1) + "01T000000"; 
				to += cal.get(GregorianCalendar.YEAR) + "0" + (month+1);
			}else{
				from += cal.get(GregorianCalendar.YEAR) + "" + (month+1) + "01T000000"; 
				to += cal.get(GregorianCalendar.YEAR) + "" + (month+1);
			}
			int odd = month%2;
			
			if(month<=6){
				if(month == 1){
					if(cal.isLeapYear(cal.get(GregorianCalendar.YEAR)))
						to += "29T235959";
					else
						to += "28T235959";
				}else{
					if(odd==0){
						to += "31T235959";
					} else 
						to += "30T235959";
				}
			}else{
				if(odd==1){
					to += "31T235959";
				} else 
					to += "30T235959";
			}
		} else if(grName.equals("week")){
			GregorianCalendar cal_help = new GregorianCalendar();
			cal_help.setTimeInMillis(cal.getTimeInMillis());
			cal_help.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.MONDAY);
			int month = (cal_help.get(GregorianCalendar.MONTH)+1);
			if(month<10)
				from += cal_help.get(GregorianCalendar.YEAR) + "0" + month;
			else
				from += cal_help.get(GregorianCalendar.YEAR) + "" + month;
			int day = cal_help.get(GregorianCalendar.DAY_OF_MONTH);
			if(day<10)
				from += "0" + day + "T000000"; 
			else
				from += "" + day + "T000000"; 
			
			cal_help.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.SUNDAY);
			month = (cal_help.get(GregorianCalendar.MONTH)+1);
			if(month<10)
				to += cal_help.get(GregorianCalendar.YEAR) + "0" + month;
			else
				to += cal_help.get(GregorianCalendar.YEAR) + "" + month;
			day = cal_help.get(GregorianCalendar.DAY_OF_MONTH);
			if(day<10)
				to += "0"+ day + "T235959"; 
			else
				to += day + "T235959";
			
		} else if(grName.equals("day")){
			int month = (cal.get(GregorianCalendar.MONTH)+1);
			if(month<10){
				from += cal.get(GregorianCalendar.YEAR) + "0" + month;
				to += cal.get(GregorianCalendar.YEAR) + "0" + month;
			}else{
				from += cal.get(GregorianCalendar.YEAR) + "" + month;
				to += cal.get(GregorianCalendar.YEAR) + "" + month;
			}
			int day = cal.get(GregorianCalendar.DAY_OF_MONTH);
			if(day<10){
				from += "0" + day + "T000000"; 
				to += "0" + day + "T235959";
			}
			else{
				from += day + "T000000";
				to += day + "T235959";
			}
		} else {
			System.out.println( "warning: default granularity reads in maximal 5 years at the moment!" );
			from += ((cal.get(GregorianCalendar.YEAR)-2) + "0101T000000" );
			to += ((cal.get(GregorianCalendar.YEAR)+3) + "1231T235959" );
		}
		
		Granularity xGranularity = axes.get(grName).get(0).granularity;
		Granularity yGranularity = axes.get(grName).get(1).granularity;

		iCalendarImporter importer = new iCalendarImporter(from,to,xGranularity,yGranularity,today);
		
		return importer.readData(path);
	}
	
	/**
	 * this methods get the first date in a view of a specific granularity
	 * to generate the date labels
	 * @param grName granularity name
	 * @param firstGranularity xaxis granularity
	 * @param secondGranularity y axis granularity
	 * @param current milliseconds of current time in calendar (see actualday)
	 * @return a String e.g. "01.01.2012"
	 * @throws TemporalDataException
	 */
	public String getFirstDate( String grName, int firstGranularity, int secondGranularity, long current ) throws TemporalDataException{
		String def = "";
		GregorianCalendar cal = new GregorianCalendar();
		cal.setTimeInMillis(current);
		
		switch(Granularities.fromInt(firstGranularity)){
			case Hour: {
				switch(Granularities.fromInt(secondGranularity)){
					case Day: {
						String str = "";
						if(grName.equals("week"))
							cal.set(GregorianCalendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
						
						int day = cal.get(GregorianCalendar.DAY_OF_MONTH);
						if(day < 10)
							str += "0" + day + ".";
						else
							str += day + ".";
						int month = (cal.get(GregorianCalendar.MONTH)+1);
						if(month < 10)
							str += "0" + month + ".";
						else
							str += month + ".";
						str += cal.get(GregorianCalendar.YEAR);
						
						return str;
					}
					default:{
						System.out.println( "Granularity combination not supported at the moment!" );
						break;
					}
				}
				break;
			}
			case Day: {
				switch(Granularities.fromInt(secondGranularity)){
				case Week: {
					String str = "";
					cal.set(GregorianCalendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
					cal.set(GregorianCalendar.WEEK_OF_MONTH, 0);
					int day = cal.get(GregorianCalendar.DAY_OF_MONTH);
					if(day < 10)
						str += "0" + day + ".";
					else
						str += day + ".";
					int month = (cal.get(GregorianCalendar.MONTH)+1);
					if(month < 10)
						str += "0" + month + ".";
					else
						str += month + ".";
					str += cal.get(GregorianCalendar.YEAR);
					
					return str;
				}
				case Month: {
					String str = "";
					str += "01" + ".";
					str += "01" + ".";
					str += cal.get(GregorianCalendar.YEAR);
					//System.out.println( cal.getTime() );
					return str;
				}
				default:{
					System.out.println( "Granularity combination not supported at the moment!" );
					break;
				}
			}
		}
		default:{
			System.out.println( "Granularity combination not supported at the moment!" );
			break;
		}
		}
		return def;
	}
	
	/**
	 * generates date labels for each field in the grid of the calendar in all
	 * standard views (day, week, month, year)
	 * @param axes axes definition from CalendarVisualization
	 * @param grName granualrity name
	 * @param kw_str calendar week number
	 * @param today milliseconds of the current time in the calendar
	 * @return TemporalDataset the dates for each field in the grid, whereas one grid
	 * is specified as for instance 01.01.2012
	 * @throws ParseException
	 * @throws TemporalDataException
	 */
	public TemporalDataset generateDateTable(HashMap<String,ArrayList<CalendarAxis>> axes, String grName, String kw_str, long today) throws ParseException, TemporalDataException{
		TemporalDataset date = new TemporalDataset();
		date.addDataColumn("datum", String.class, "");
		date.addDataColumn("xstart", float.class, -1);
		date.addDataColumn("xend", float.class, -1);
		date.addDataColumn("ystart", float.class, -1);
		date.addDataColumn("yend", float.class, -1);
		
		Granularity xGranularity = axes.get(grName).get(0).granularity;
		Granularity yGranularity = axes.get(grName).get(1).granularity;

		int xrange [] = {0,axes.get(grName).get(0).specification.perpage};
		int yrange [] = {0,axes.get(grName).get(1).specification.perpage};
	
		Date start = new Date();
		int contextId = 0;
		String datum = "";
		if(xGranularity.getIdentifier() > yGranularity.getIdentifier()){
			int kw = 0;
			if(  kw_str.equals("") ){
				GregorianCalendar cal = new GregorianCalendar();
				cal.setTimeInMillis(today);
				kw = cal.get(GregorianCalendar.WEEK_OF_YEAR);
			} else
				kw = Integer.parseInt(kw_str);
			
			datum = getFirstDate( grName, yGranularity.getIdentifier(), xGranularity.getIdentifier(), today );
			
			if(datum.length() == "dd.MM.yyyy".length()){
				start = new SimpleDateFormat("dd.MM.yyyy").parse(datum);
			}
			contextId = xGranularity.getIdentifier();
		} else {
			int kw = 0;
			if(  kw_str.equals("") ){
				GregorianCalendar cal = new GregorianCalendar();
				cal.setTimeInMillis(today);
				kw = cal.get(GregorianCalendar.WEEK_OF_YEAR);
			}else
				kw = Integer.parseInt(kw_str);
				
			datum = getFirstDate( grName, xGranularity.getIdentifier(), yGranularity.getIdentifier(), today );
			if(datum.length() == "dd.MM.yyyy".length()){
				start = new SimpleDateFormat("dd.MM.yyyy").parse(datum);
			}
			contextId = yGranularity.getIdentifier();
		}
		
		JavaDateCalendarManager manager = new JavaDateCalendarManager();
		Granule startGranule;
		TemporalElement te0;
		TemporalObject o0;
		GregorianCalendar cal = new GregorianCalendar();
		cal.setTime(start);

		boolean pm = false;
		boolean flag = false;
		long mill = 0;
		for (int i = xrange[0]; i < xrange[1]; i++) {
			if(yGranularity.getGranularityContextIdentifier() == Granularities.Month.toInt()){
				mill = cal.getTimeInMillis();
			}
			
			for (int j = yrange[0]; j < yrange[1]; j++) {
				startGranule = manager.createGranule(cal.getTime(), new Granularity(xGranularity.getCalendar(),0,contextId));
				te0 = date.addTemporalElement(startGranule.getInf(), startGranule.getSup(), 0, contextId, PRIMITIVE_INTERVAL);
				o0 = date.addTemporalObject(te0.getId());
				o0.set("datum", cal.get(GregorianCalendar.DAY_OF_MONTH) + "." + (cal.get(GregorianCalendar.MONTH)+1) + "." + cal.get(GregorianCalendar.YEAR));
				o0.set("xstart", i);
				o0.set("xend", i+1);
				o0.set("ystart", j);
				o0.set("yend", j+1);
				
				if(yGranularity.getIdentifier() == Granularities.Hour.toInt() ){
					int hour = cal.get(GregorianCalendar.HOUR)+1;
					if(hour == 12){
						hour = 0;
						if(!pm) pm = true;
						else pm = false;
					}
					cal.set(GregorianCalendar.HOUR, hour);
				} else if(yGranularity.getIdentifier() == Granularities.Week.toInt() ){
					int week = cal.get(GregorianCalendar.WEEK_OF_MONTH)+1;
					if(week == 7){
						week = 0;
					}
					cal.set(GregorianCalendar.WEEK_OF_MONTH, week);
			
				} else if(yGranularity.getIdentifier() == Granularities.Month.toInt() && 
					      yGranularity.getGranularityContextIdentifier() == Granularities.Year.toInt() ){
					int month = cal.get(GregorianCalendar.MONTH)+1;
					int day = cal.get(GregorianCalendar.DAY_OF_MONTH);
					if(month == 12){
						month = 0;
					}
					
					if(day == 29)
						flag = true;
					if( flag ){
						day = i+1;
					}
					cal.set(GregorianCalendar.MONTH, month);
					cal.set(GregorianCalendar.DAY_OF_MONTH, day);
					if(cal.get(GregorianCalendar.MONTH) != month)
						cal.set(GregorianCalendar.MONTH, month );
				} else
					System.out.println( "Granularity not supported for Datum!" );
			}
			
			if(yGranularity.getGranularityContextIdentifier() == Granularities.Month.toInt()){
				cal.setTimeInMillis(mill);
			}
			
			if(xGranularity.getIdentifier() == Granularities.Day.toInt() && 
			   yGranularity.getGranularityContextIdentifier() == Granularities.Week.toInt() ){
				int day = cal.get(GregorianCalendar.DAY_OF_WEEK) + 1;
				if(day == 7) day = 0;
				cal.set(GregorianCalendar.DAY_OF_WEEK, day);
			} else if(xGranularity.getIdentifier() == Granularities.Day.toInt() && 
					yGranularity.getGranularityContextIdentifier() == Granularities.Day.toInt() ){
				// do nothing, stay at the same day
			} else if(xGranularity.getIdentifier() == Granularities.Day.toInt() && 
					yGranularity.getGranularityContextIdentifier() == Granularities.Month.toInt() ){
					int day = cal.get(GregorianCalendar.DAY_OF_WEEK) + 1;
					if(day == 7) {
						day = 0;
					}
					cal.set(GregorianCalendar.DAY_OF_WEEK, day);
			} else if(xGranularity.getIdentifier() == Granularities.Day.toInt() && 
					yGranularity.getGranularityContextIdentifier() == Granularities.Year.toInt() ){
					int day = cal.get(GregorianCalendar.DAY_OF_MONTH) + 1;
					int month = cal.get(GregorianCalendar.MONTH);
					int odd = month%2;
					
					if(month<=6){
						if(month == 1){
							if(cal.isLeapYear(cal.get(GregorianCalendar.YEAR)))
								if(day == 30) {day = 1;}
							else
								if(day == 29) {day = 1;}
						}else{
							if(odd==0){
								if(day == 32) {day = 1;} 
							} else 
								if(day == 31) {day = 1;}
						}
					}else{
						if(odd==1){
							if(day == 32) {day = 1;}
						} else 
							if(day == 31) {day = 1;}
					}
					
				cal.set(GregorianCalendar.DAY_OF_MONTH, day);
			}
			else
				System.out.println( "Granularity not supported for Datum!" );
		}
		
		return date;
	}
}